﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using DeferredWaterDemo;

namespace Demo
{
    public partial class Render
    {
        static GraphicsDevice graphics;


        static RenderTarget2D mSceneColor = null;
        static RenderTarget2D mSceneHDR = null;
        static RenderTarget2D mEncodeNormal = null;
        static RenderTarget2D mEncodeDepth = null; 
        static RenderTarget2D rt_Diffuse = null;
        static RenderTarget2D rt_Specular = null;

        static EffectParameter ssp_InvViewProj;
        static EffectParameter ssp_InvView;
        static EffectParameter ssp_Projection;
        static EffectParameter ssp_View;
        static EffectParameter ssp_Time;
        static EffectParameter ssp_LightPosition;
        static EffectParameter ssp_LightColor;
        static EffectParameter ssp_LightRange;
        static Effect mDeferredShader = null;


        public static GraphicsDevice GraphicsDevice
        {
            get
            {
                return graphics;
            }
        }
        
        public static void UpdateShaderParameters()
        {
            float t = Core.Time;
            int d = (int)(t / (2 * (float)Math.PI));
            t -= (d * 2 * (float)Math.PI);


            mDeferredShader.Parameters["Phase"].SetValue(t);

            Matrix ViewProjection = Core.Camera.View * Core.Camera.Projection;

            ssp_Time.SetValue(Core.Time / 35.0f);
            ssp_Projection.SetValue(Core.Camera.Projection);
            ssp_View.SetValue(Core.Camera.View);
            ssp_InvViewProj.SetValue(Matrix.Invert(ViewProjection));
            ssp_InvView.SetValue(Matrix.Invert(Core.Camera.View));
        }

        public static bool Release()
        {
            if (sprite != null)
            {
                sprite.Dispose();
                sprite = null;
            }
            graphics = null; 

            return false;
        }

        private static bool LoadDeferredShader()
        {
            Core.Demo.Window.Title = "Loading...";
            
            Effect tmp = null;
            if (EngineResource.LoadShader(".\\Content\\Shaders\\Deferred.fx", out tmp) == false)
                return false;

            mDeferredShader = tmp;
            ssp_Time = mDeferredShader.Parameters["Time"];
            ssp_View = mDeferredShader.Parameters["View"];
            ssp_Projection = mDeferredShader.Parameters["Projection"];
            ssp_InvViewProj = mDeferredShader.Parameters["InvViewProjection"];
            ssp_InvView = mDeferredShader.Parameters["InvView"];
            mDeferredShader.CurrentTechnique = mDeferredShader.Techniques[0];
            Core.Demo.Window.Title = "rt-deferredwater";
            return true;
        }

        public static bool Initialize(Game game)
        {
            graphics = game.GraphicsDevice;
            sprite = new SpriteBatch(graphics);

            int width = graphics.Viewport.Width;
            int height = graphics.Viewport.Height;

            if (!LoadDeferredShader())
                return false;         

            mEncodeNormal = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Color);
            rt_Diffuse = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Color);
            mSceneColor = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Color);
            mSceneHDR = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Color);
            mEncodeDepth = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Single); 
            rt_Specular = Render.CreateRenderTarget(graphics, 1, SurfaceFormat.Color);

            VertexPositionTexture[] quad = new VertexPositionTexture[6];
            Array.Clear(quad, 0, 6);
            quad[0].TextureCoordinate = new Vector2(-1, 1); 
            quad[1].TextureCoordinate = new Vector2(1, 1); 
            quad[2].TextureCoordinate = new Vector2(1, -1);
 
            quad[3].TextureCoordinate = new Vector2(-1, 1); 
            quad[4].TextureCoordinate = new Vector2(1, -1); 
            quad[5].TextureCoordinate = new Vector2(-1, -1);

            screenQuad = new VertexBuffer(graphics, VertexPositionTexture.SizeInBytes * 6, BufferUsage.WriteOnly);
            screenQuad.SetData<VertexPositionTexture>(quad); 

            return true;
        }

        private static void DrawRenderTargets()
        {
            Texture2D[] renderTargets = { 
                mEncodeDepth.GetTexture(),
                mEncodeNormal.GetTexture(),
                mSceneColor.GetTexture() };

            int i = 0, padding = 5, height = 95;

            Render.Sprites.Begin();
            foreach (Texture2D renderTarget in renderTargets)
            {
                int width = (int)((float)(height * renderTarget.Width) / (float)renderTarget.Height);
                Render.Sprites.Draw(renderTarget, new Rectangle(padding + i, padding, width, height), Color.White);
                i += (width + padding);
            }
            Render.Sprites.End();
        }

        public delegate void OnDraw(Effect overrideShader);
        public delegate void OnPreDeferred();

        public static bool Draw(OnDraw drawFunction, OnPreDeferred predeferredFunction)
        {
            if (Core.Demo.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space) && !LoadDeferredShader())
                return false;
            if (Core.IsNull(drawFunction))
                return false;

            UpdateShaderParameters();

            for (int i = 0; i < 9; i++)
            {
                GraphicsDevice.SamplerStates[i].MagFilter = 
                GraphicsDevice.SamplerStates[i].MinFilter =
                GraphicsDevice.SamplerStates[i].MipFilter = TextureFilter.Linear;
                GraphicsDevice.SamplerStates[i].AddressU = TextureAddressMode.Wrap;
                GraphicsDevice.SamplerStates[i].AddressV = TextureAddressMode.Wrap;
                GraphicsDevice.Textures[i] = null;
            }

            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = false;

            /*
             * Draw the scene.
             * 
             * The scene is rendered to two render targets. 
             * The first stores a standard color image of the scene.
             * The second stores bloom/hdr data of the scene.
             */
            GraphicsDevice.VertexDeclaration = Render.GeometryVertex;

            GraphicsDevice.SetRenderTarget(0, mSceneColor);
            GraphicsDevice.SetRenderTarget(1, mSceneHDR);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
            //GraphicsDevice.RenderState.ReferenceStencil = 1;

            drawFunction(null);

            /*
             * Encode the scene.
             * 
             * The scene is encoded to two render targets. 
             * The first stores a depth/distance from the camera's near clip plane to corresponding surface points.
             * The second stores a surface orrientation (normals) from the scene's perspective.
             */
            GraphicsDevice.SetRenderTarget(0, mEncodeDepth);
            GraphicsDevice.SetRenderTarget(1, mEncodeNormal); 
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);

            mDeferredShader.Begin();
            mDeferredShader.CurrentTechnique.Passes[0].Begin();

            drawFunction(mDeferredShader);

            mDeferredShader.CurrentTechnique.Passes[0].End();
            mDeferredShader.End();

            /*
             * Present the final result.
             * 
             * The deferred water computations now occour. The final result is presented to the display device.
             */
            GraphicsDevice.VertexDeclaration = Render.ScreenVertex;
            GraphicsDevice.Vertices[0].SetSource(screenQuad, 0, VertexPositionTexture.SizeInBytes);

            GraphicsDevice.SetRenderTarget(0, null);
            GraphicsDevice.SetRenderTarget(1, null); 
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);

            GraphicsDevice.Textures[0] = mEncodeDepth.GetTexture();
            GraphicsDevice.Textures[1] = mEncodeNormal.GetTexture();
            GraphicsDevice.Textures[2] = mSceneColor.GetTexture();
            GraphicsDevice.Textures[3] = mSceneHDR.GetTexture(); 

            if (!Core.IsNull(predeferredFunction))
                predeferredFunction();

            mDeferredShader.Begin();
            mDeferredShader.CurrentTechnique.Passes[1].Begin();

            GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);

            mDeferredShader.CurrentTechnique.Passes[1].End();
            mDeferredShader.End();

            DrawRenderTargets();

            return true;
        }
        /*
         public static bool RenderDeferred()
        {
            if (InputEngine.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space) && !LoadDeferredShader())
                return false;

            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = false;

            currentOverride = deferredShader;
            GraphicsDevice.SetRenderTarget(0, rg_Depth);
            GraphicsDevice.SetRenderTarget(1, rt_Normal);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);

            GraphicsDevice.VertexDeclaration = Render.EngineGeoemtry;
            deferredShader.CurrentTechnique = deferredShader.Techniques[0];
            deferredShader.Begin();
            deferredShader.CurrentTechnique.Passes[0].Begin();
            Engine.DrawScene();
            deferredShader.CurrentTechnique.Passes[0].End();
            deferredShader.End();


            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = true;

            GraphicsDevice.SetRenderTarget(0, rt_Diffuse);
            GraphicsDevice.SetRenderTarget(1, rt_Specular);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.TransparentBlack, 1f, 0);

            GraphicsDevice.Textures[0] = rt_Normal.GetTexture();
            GraphicsDevice.Textures[1] = rg_Depth.GetTexture();
            GraphicsDevice.Textures[2] = sky;
            GraphicsDevice.Textures[3] = water_bump;
            GraphicsDevice.Textures[4] = rt_Scene.GetTexture();
            GraphicsDevice.SamplerStates[4].AddressU =
            GraphicsDevice.SamplerStates[4].AddressV = TextureAddressMode.Clamp;

            for(int i = 0; i < 4; i++)
            GraphicsDevice.SamplerStates[i].MagFilter =
            GraphicsDevice.SamplerStates[i].MinFilter =
            GraphicsDevice.SamplerStates[i].MipFilter = TextureFilter.Linear;

            GraphicsDevice.VertexDeclaration = Render.EngineGeoemtry;
            
            GraphicsDevice.RenderState.DepthBufferEnable = false;

            deferredShader.Begin();
            deferredShader.CurrentTechnique.Passes[1].Begin();
            
            Engine.DrawScene();

            deferredShader.CurrentTechnique.Passes[1].End();
            deferredShader.End(); 
            GraphicsDevice.RenderState.DepthBufferEnable = true;

            GraphicsDevice.SamplerStates[4].AddressU =
            GraphicsDevice.SamplerStates[4].AddressV = TextureAddressMode.Wrap;

            GraphicsDevice.SetRenderTarget(0, null);
            GraphicsDevice.SetRenderTarget(1, null);
            currentOverride = null; 
            return true;
        }
        
         */
    /*    public static bool RenderDeferred()
        {
            if (Core.Demo.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space) && !LoadDeferredShader())
                return false;

            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = false;

            currentOverride = deferredShader;
            GraphicsDevice.SetRenderTarget(0, rg_Depth);
            GraphicsDevice.SetRenderTarget(1, rt_Normal);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);

            GraphicsDevice.VertexDeclaration = Render.EngineGeoemtry;
            deferredShader.CurrentTechnique = deferredShader.Techniques[0];
            deferredShader.Begin();
            deferredShader.CurrentTechnique.Passes[0].Begin();
            Core.DrawScene();
            deferredShader.CurrentTechnique.Passes[0].End();
            deferredShader.End();


            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = true;

            //GraphicsDevice.SetRenderTarget(0, rt_Diffuse);
            //GraphicsDevice.SetRenderTarget(1, rt_Specular);
            GraphicsDevice.SetRenderTarget(0, null);
            GraphicsDevice.SetRenderTarget(1, null);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.TransparentBlack, 1f, 0);

            GraphicsDevice.Textures[0] = rt_Normal.GetTexture();
            GraphicsDevice.Textures[1] = rg_Depth.GetTexture();
            GraphicsDevice.Textures[2] = sky;
            GraphicsDevice.Textures[5] = water_height;
            GraphicsDevice.Textures[4] = water_bump;
            GraphicsDevice.SamplerStates[4].AddressU =
            GraphicsDevice.SamplerStates[4].AddressV = TextureAddressMode.Wrap;

            GraphicsDevice.Textures[3] = mSceneColor.GetTexture();
            GraphicsDevice.SamplerStates[3].AddressU =
            GraphicsDevice.SamplerStates[3].AddressV = TextureAddressMode.Clamp;

            for(int j = 0; j < 6; j++)
            GraphicsDevice.SamplerStates[j].MagFilter =
            GraphicsDevice.SamplerStates[j].MinFilter =
            GraphicsDevice.SamplerStates[j].MipFilter = TextureFilter.Linear;

            GraphicsDevice.VertexDeclaration = Render.EngineGeoemtry;
            
            GraphicsDevice.RenderState.DepthBufferEnable = false;

            deferredShader.Begin();
            deferredShader.CurrentTechnique.Passes[1].Begin();
            
            Core.DrawScene();

            deferredShader.CurrentTechnique.Passes[1].End();
            deferredShader.End(); 
            GraphicsDevice.RenderState.DepthBufferEnable = true;

            GraphicsDevice.SamplerStates[3].AddressU =
            GraphicsDevice.SamplerStates[3].AddressV = TextureAddressMode.Wrap;

            //GraphicsDevice.SetRenderTarget(0, null);
            //GraphicsDevice.SetRenderTarget(1, null);
            currentOverride = null;

            Render.Sprites.Begin();
            Texture2D[] views = { 
                rg_Depth.GetTexture(),
                rt_Normal.GetTexture(),
                mSceneColor.GetTexture() };
            int i = 0, width = 100, height = 75;
            foreach (Texture2D view in views)
            {
                Render.Sprites.Draw(
                    view,
                    new Rectangle(5 + i, 5, width, height),
                    Color.White);
                i += width + 5;
            }
            Render.Sprites.End();

            return true;
        }
      */  public static void RenderPostProcess()
        {
            /*
            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.AlphaBlendEnable = true;

            GraphicsDevice.Textures[0] = rt_Diffuse.GetTexture();
            GraphicsDevice.Textures[1] = rt_Specular.GetTexture();
            GraphicsDevice.Textures[2] = rt_Scene.GetTexture();

            GraphicsDevice.VertexDeclaration = Render.EngineLighting;
            GraphicsDevice.Vertices[0].SetSource(quadVB, 0, VertexPositionTexture.SizeInBytes);

            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);

            deferredShader.Begin();
            deferredShader.CurrentTechnique.Passes[2].Begin();

            GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);

            deferredShader.CurrentTechnique.Passes[2].End();
            deferredShader.End();

            Render.Sprites.Begin();
            Texture2D[] views = { 
                rg_Depth.GetTexture(),
                rt_Normal.GetTexture(),
                rt_Diffuse.GetTexture() };
            int i = 0, width = 100, height = 75;
            foreach (Texture2D view in views)
            {
                Render.Sprites.Draw(
                    view,
                    new Rectangle(5 + i, 5, width, height),
                    Color.White);
                i += width + 5;
            }
            Render.Sprites.End();
            */
        }
         
    }
}
